/**
 * 迁移核心模块
 */
const path = require("path")
const fs = require("fs")
const { analyzeFile } = require("./analyzers")
const { resolveDependencyPath } = require("./resolvers")
const { generateReport } = require("./reporters")
const { getAllFiles, copyFile, getRelativePath } = require("./utils/fileUtils")
const { detectProjectConfig, mergeConfig } = require("./utils/configUtils")
const chalk = require("chalk")

/**
 * 迁移工具类
 */
class Migrator {
  /**
   * 创建迁移工具实例
   * @param {Object} options - 选项
   */
  constructor(options = {}) {
    // 源路径
    this.sourcePath = path.resolve(options.sourcePath || "./src")
    // 入口点
    this.entryPoint = path.resolve(options.entryPoint || "./src/index.js")
    // 目标路径
    this.targetPath = path.resolve(options.targetPath || "./dist")

    // 检测项目配置
    const detectedConfig = detectProjectConfig(path.dirname(this.sourcePath))

    // 合并配置
    this.config = mergeConfig({
      ...options,
      ...detectedConfig,
    })

    // 存储已处理的文件路径
    this.processedFiles = new Set()
    // 存储依赖关系
    this.dependencyMap = new Map()
  }

  /**
   * 执行迁移流程
   */
  async migrate() {
    console.log(chalk.blue("=== 文件迁移工具 ==="))
    console.log(`源路径: ${chalk.yellow(this.sourcePath)}`)
    console.log(`入口点: ${chalk.yellow(this.entryPoint)}`)
    console.log(`目标路径: ${chalk.yellow(this.targetPath)}`)

    // 确保目标目录存在
    if (!fs.existsSync(this.targetPath)) {
      fs.mkdirSync(this.targetPath, { recursive: true })
    }

    const startTime = Date.now()

    try {
      // 检查入口点是否存在
      if (!fs.existsSync(this.entryPoint)) {
        throw new Error(`入口点不存在: ${this.entryPoint}`)
      }

      // 分析入口点
      if (fs.statSync(this.entryPoint).isDirectory()) {
        // 目录入口
        await this.processDirectoryEntry()
      } else {
        // 文件入口
        await this.processFileEntry()
      }

      // 生成报告
      if (this.config.report.generateReport) {
        generateReport(this.dependencyMap, this.config.report.outputPath, {
          projectRoot: this.sourcePath,
        })
      }

      const duration = ((Date.now() - startTime) / 1000).toFixed(2)
      console.log(chalk.green("\n迁移完成!"))
      console.log(`共迁移 ${chalk.cyan(this.processedFiles.size)} 个文件`)
      console.log(`用时: ${chalk.cyan(duration)} 秒`)
    } catch (error) {
      console.error(chalk.red("迁移过程中出错:"), error.message)
      process.exit(1)
    }
  }

  /**
   * 处理目录入口
   */
  async processDirectoryEntry() {
    console.log(chalk.yellow("入口点是目录，查找所有文件..."))

    // 获取目录下的所有文件
    const files = getAllFiles(this.entryPoint, {
      extensions: this.config.extensions,
      ignorePaths: this.config.ignorePaths,
    })

    console.log(`共找到 ${chalk.cyan(files.length)} 个文件`)

    // 分析所有文件依赖
    let allDependencies = new Set()

    for (const file of files) {
      if (this.config.extensions.includes(path.extname(file))) {
        console.log(`处理入口文件: ${chalk.cyan(file)}`)
        const deps = await this.analyzeDependencies(file)
        deps.forEach((d) => allDependencies.add(d))
      } else {
        // 非代码文件直接添加
        allDependencies.add(path.resolve(file))
      }
    }

    // 复制入口目录结构
    const entryDirRelative = path.relative(
      path.resolve(this.sourcePath),
      path.resolve(this.entryPoint)
    )
    const entryDirTarget = path.join(this.targetPath, entryDirRelative)

    // 确保入口目录结构被复制
    if (!fs.existsSync(entryDirTarget)) {
      fs.mkdirSync(entryDirTarget, { recursive: true })
    }

    // 复制所有依赖文件
    await this.copyFilesToTarget(Array.from(allDependencies))
  }

  /**
   * 处理文件入口
   */
  async processFileEntry() {
    console.log(`分析入口文件: ${chalk.cyan(this.entryPoint)}`)

    // 分析依赖
    const dependencies = await this.analyzeDependencies(this.entryPoint)

    // 复制文件
    await this.copyFilesToTarget(dependencies)
  }

  /**
   * 递归分析文件依赖
   * @param {string} filePath - 文件路径
   * @returns {Array<string>} 依赖文件路径数组
   */
  async analyzeDependencies(filePath) {
    const queue = [path.resolve(filePath)]
    const allDependencies = new Set()

    while (queue.length > 0) {
      const currentFile = queue.shift()

      // 跳过已处理文件
      if (this.processedFiles.has(currentFile)) {
        continue
      }

      // 标记文件为已处理
      this.processedFiles.add(currentFile)
      allDependencies.add(currentFile)

      // 查找文件依赖
      const importPaths = await this.findDependencies(currentFile)

      // 将依赖信息存入映射表
      this.dependencyMap.set(
        currentFile,
        importPaths.map((p) => p.source)
      )

      // 将解析到的依赖添加到队列
      for (const importInfo of importPaths) {
        if (
          importInfo.resolved &&
          !this.config.ignorePaths.some((p) => importInfo.resolved.includes(p))
        ) {
          queue.push(importInfo.resolved)
        }
      }
    }

    return Array.from(allDependencies)
  }

  /**
   * 查找文件中的依赖
   * @param {string} filePath - 文件路径
   * @returns {Array<Object>} 依赖信息数组，包含源路径和解析后路径
   */
  async findDependencies(filePath) {
    try {
      // 分析文件中的import语句
      const dependencies = analyzeFile(filePath)

      // 解析依赖路径
      return dependencies.map((dep) => {
        const resolved = resolveDependencyPath(
          dep,
          filePath,
          this.config,
          this.sourcePath
        )
        return {
          source: dep,
          resolved,
        }
      })
    } catch (error) {
      console.error(
        chalk.red(`分析文件 ${filePath} 依赖时出错:`),
        error.message
      )
      return []
    }
  }

  /**
   * 复制文件到目标目录
   * @param {Array<string>} filePaths - 文件路径数组
   */
  async copyFilesToTarget(filePaths) {
    console.log(chalk.yellow("开始复制文件..."))

    let successCount = 0
    let failCount = 0

    for (const filePath of filePaths) {
      try {
        // 获取相对于源目录的路径
        const relativePath = getRelativePath(this.sourcePath, filePath)
        const targetFilePath = path.join(this.targetPath, relativePath)

        // 复制文件
        const success = copyFile(filePath, targetFilePath)

        if (success) {
          successCount++
          console.log(`已复制: ${chalk.green(relativePath)}`)
        } else {
          failCount++
        }
      } catch (error) {
        failCount++
        console.error(chalk.red(`复制文件 ${filePath} 时出错:`), error.message)
      }
    }

    console.log(
      `成功复制 ${chalk.green(successCount)} 个文件，失败 ${chalk.red(
        failCount
      )} 个文件`
    )
  }
}

module.exports = Migrator
